package com.demarc.pesquisas.negocio.dao;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.demarc.pesquisas.infraestrutura.persistencia.ColecaoChecada;
import com.demarc.pesquisas.infraestrutura.persistencia.Filtro;
import com.demarc.pesquisas.infraestrutura.persistencia.ResultadoPaginado;
import com.demarc.pesquisas.negocio.dto.EntrevistaDTO;
import com.demarc.pesquisas.negocio.dto.RelatorioAgrupamentoLocalidadeDTO;
import com.demarc.pesquisas.negocio.dto.RelatorioFiltroDTO;
import com.demarc.pesquisas.negocio.entidade.TipoLocalidadeEnum;
import com.demarc.pesquisas.negocio.entidade.domicilio.Domicilio;
import com.demarc.pesquisas.negocio.entidade.entrevista.Entrevista;
import com.demarc.pesquisas.negocio.entidade.entrevista.EntrevistaFiltro;
import com.demarc.pesquisas.negocio.entidade.entrevistado.Entrevistado;
import com.demarc.pesquisas.negocio.entidade.pesquisa.Pesquisa;

@Repository
public class EntrevistaDAO extends AbstractDAO<Entrevista, Long> {

    private static final String ALIAS_DOM_RENDA_FAMILIAR = "dom.rendaFamiliar";
    private static final String ALIAS_TIPO_LOCALIDADE = "domicilio.endereco.tipoLocalidade";
    private static final String ALIAS_LOCALIDADE = "domicilio.endereco.localidade";

    @Transactional(readOnly = true)
    public boolean existeSequencialIgual(Entrevista entrevista) {
        Criteria criteria = createCriteria();
        criteria.createAlias("pesquisa", "pesquisa");

        criteria.add(Restrictions.eq("pesquisa.id", entrevista.getPesquisa().getId()));
        criteria.add(Restrictions.eq("sequencial", entrevista.getSequencial()));
        if (entrevista.getId() != null) {
            criteria.add(Restrictions.ne("id", entrevista.getId()));
        }
        return existeResultado(criteria);
    }

    @Transactional(readOnly = true)
    public boolean entrevistadoJaCadastradoComCPF(Entrevista entrevista) {
        Criteria criteria = createCriteria();
        criteria.createAlias("pesquisa", "pesquisa");
        criteria.createAlias("entrevistado", "entrevistado");

        criteria.add(Restrictions.eq("pesquisa.id", entrevista.getPesquisa().getId()));
        criteria.add(Restrictions.eq("entrevistado.cpf", entrevista.getEntrevistado().getCpf()));
        if (entrevista.getId() != null) {
            criteria.add(Restrictions.ne("id", entrevista.getId()));
        }
        return existeResultado(criteria);
    }

    @Transactional(readOnly = true)
    public ResultadoPaginado<EntrevistaDTO> consultar(EntrevistaFiltro entrevistaFiltro) {

        int quantidade = this.quantidadeLinhas(entrevistaFiltro);
        List<EntrevistaDTO> resultado = new ArrayList<EntrevistaDTO>();
        if (quantidade > 0) {
            Criteria criteria = createCriteria();
            this.adicionarRestrincoes(entrevistaFiltro, criteria);

            Filtro filtro = entrevistaFiltro.getFiltro();
            super.adicionarOrdenacao(criteria, filtro.getOrdem(), new HashSet<String>());
            criteria.setFirstResult(filtro.getPrimeiroElemento());
            criteria.setMaxResults(filtro.getQuantidadeMaximaResultados());

            criteria.setProjection(Projections.projectionList()
                    .add(Projections.property("id"), "id")
                    .add(Projections.property("sequencial"), "sequencial")
                    .add(Projections.property("entdo.nome"), "nomeEntrevistado")
                    .add(Projections.property("entdo.cpf"), "cpfEntrevistado")
                    .add(Projections.property(ALIAS_DOM_RENDA_FAMILIAR), "rendaFamiliar")
                    .add(Projections.property("dom.endereco.localidade"), "localidade")
                    .add(Projections.property("dom.endereco.tipoLocalidade"), "tipoLocalidade")
                    .add(Projections.property("data"), "data")
                    .add(Projections.property("entdor.nome"), "nomeEntrevistador"));

            criteria.setResultTransformer(new AliasToBeanResultTransformer(EntrevistaDTO.class));
            resultado = ColecaoChecada.checkedList(criteria.list(), EntrevistaDTO.class);
        }
        return new ResultadoPaginado<EntrevistaDTO>(resultado, quantidade);
    }

    private int quantidadeLinhas(EntrevistaFiltro entrevistaFiltro) {
        Criteria quantidadeCriteria = createCriteria();
        adicionarRestrincoes(entrevistaFiltro, quantidadeCriteria);
        int quantidade = getTotalRegistros(quantidadeCriteria).intValue();
        return quantidade;
    }

    private void adicionarRestrincoes(EntrevistaFiltro entrevistaFiltro, Criteria criteria) {

        criteria.createAlias("pesquisa", "pes");
        criteria.createAlias("entrevistado", "entdo");
        criteria.createAlias("domicilio", "dom");
        criteria.createAlias("entrevistador", "entdor");
        criteria.add(Restrictions.eq("pes.id", entrevistaFiltro.getPesquisa().getId()));

        if (entrevistaFiltro.getSequencial() != null) {
            criteria.add(Restrictions.eq("sequencial", entrevistaFiltro.getSequencial()));
        }

        Entrevistado entrevistado = entrevistaFiltro.getEntrevistado();
        if (StringUtils.isNotBlank(entrevistado.getNome())) {
            criteria.add(Restrictions.ilike("entdo.nome", entrevistado.getNome(),
                    MatchMode.ANYWHERE));
        }

        if (StringUtils.isNotBlank(entrevistado.getCpf())) {
            criteria.add(Restrictions.eq("entdo.cpf", entrevistado.getCpf()));
        }

        if (entrevistaFiltro.getData() != null) {
            criteria.add(Restrictions.eq("data", entrevistaFiltro.getData()));
        }

        if (StringUtils.isNotBlank(entrevistaFiltro.getEntrevistador().getNome())) {

            criteria.add(Restrictions.ilike("entdor.nome", entrevistaFiltro.getEntrevistador()
                    .getNome(), MatchMode.ANYWHERE));
        }

        Domicilio domicilio = entrevistaFiltro.getDomicilio();
        String localidade = domicilio.getEndereco().getLocalidade();
        if (StringUtils.isNotBlank(localidade)) {

            if (TipoLocalidadeEnum.URBANA.getCodigo().equals(localidade)) {
                criteria.add(Restrictions.eq("dom.endereco.tipoLocalidade",
                        TipoLocalidadeEnum.URBANA));

            } else if (TipoLocalidadeEnum.RURAL.getCodigo().equals(localidade)) {
                criteria.add(Restrictions.eq("dom.endereco.tipoLocalidade",
                        TipoLocalidadeEnum.RURAL));

            } else {
                criteria.add(Restrictions.eq("dom.endereco.localidade", localidade));
            }
        }

        if (domicilio.getTipoMaterialConstrucaoPredominante() != null) {
            criteria.add(Restrictions.eq("dom.tipoMaterialConstrucaoPredominante",
                    domicilio.getTipoMaterialConstrucaoPredominante()));
        }

        if (domicilio.getTipoBanheiro() != null) {
            criteria.add(Restrictions.eq("dom.tipoBanheiro", domicilio.getTipoBanheiro()));
        }

        String codigoRendaFamiliar = entrevistaFiltro.getCodigoRendaFamiliar();
        if (StringUtils.isNotBlank(codigoRendaFamiliar)) {
            if (StringUtils.equals("1", codigoRendaFamiliar)) {
                criteria.add(Restrictions.le(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("50.00"))); // menor ou igual que

            } else if (StringUtils.equals("2", codigoRendaFamiliar)) {
                criteria.add(Restrictions.ge(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("50.00"))); // maior ou igual que
                criteria.add(Restrictions.le(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("100.00"))); // menor ou igual que

            } else if (StringUtils.equals("3", codigoRendaFamiliar)) {
                criteria.add(Restrictions.ge(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("100.00")));
                criteria.add(Restrictions.le(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("200.00")));

            } else if (StringUtils.equals("4", codigoRendaFamiliar)) {
                criteria.add(Restrictions.ge(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("200.00")));
                criteria.add(Restrictions.le(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("300.00")));

            } else if (StringUtils.equals("5", codigoRendaFamiliar)) {
                criteria.add(Restrictions.ge(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("300.00")));
                criteria.add(Restrictions.le(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("400.00")));

            } else if (StringUtils.equals("6", codigoRendaFamiliar)) {
                criteria.add(Restrictions.ge(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("400.00")));
                criteria.add(Restrictions.le(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("500.00")));

            } else if (StringUtils.equals("7", codigoRendaFamiliar)) {
                criteria.add(Restrictions.gt(ALIAS_DOM_RENDA_FAMILIAR, new BigDecimal("500.00")));
            }
        }
    }

    @Transactional(readOnly = true)
    public List<String> listarLocalidades(Pesquisa pesquisa) {
        Criteria criteria = createCriteria();
        createAliasDomicilio(criteria);

        criteria.add(Restrictions.eq("pesquisa.id", pesquisa.getId()));
        criteria.setProjection(Projections.distinct(Projections.property(ALIAS_LOCALIDADE)));

        criteria.addOrder(Order.asc(ALIAS_LOCALIDADE));
        return ColecaoChecada.checkedList(criteria.list(), String.class);
    }

    @Transactional(readOnly = true)
    public List<RelatorioAgrupamentoLocalidadeDTO> consultarRelatorioTotalizador(
            RelatorioFiltroDTO filtro) {

        Criteria criteria = createCriteria();
        this.createAliasDomicilio(criteria);
        criteria.add(Restrictions.eq("pesquisa.id", filtro.getIdPesquisa()));
        this.adicionarRestrincaoLocalidadeETipo(criteria, filtro.getLocalidade());

        ProjectionList projectionList = Projections.projectionList();

        projectionList.add(Projections.property(ALIAS_LOCALIDADE), "localidade");
        projectionList.add(Projections.property(ALIAS_TIPO_LOCALIDADE), "tipoLocalidade");

        // Dados dos Domícílios
        projectionList.add(Projections.property("domicilio.id"), "id");
        projectionList.add(Projections.property("domicilio.rendaFamiliar"), "rendaFamiliar"); //BigDecimal

        // Terreno
        projectionList.add(Projections.property("domicilio.tipoTerreno"), "tipoTerreno"); //ENUM (Proprio, Cedido, Alugado)

        // Material predominante construção parede
        projectionList.add(Projections.property("domicilio.tipoMaterialConstrucaoPredominante"),
                "tipoMaterial"); //ENUM (Alvenaria, Taipa, Palha, etc)

        // Tipo de banheiros
        projectionList.add(Projections.property("domicilio.tipoBanheiro"), "tipoBanheiro"); //ENUM (qntd com fossa, qntd sem fossa, etc)

        //Armazenamento de agua
        //Cisterna
        projectionList.add(Projections.property("domicilio.possuiSisterna"), "possuiSisterna"); //ENUM 

        //Poço
        projectionList.add(Projections.property("domicilio.possuiPoco"), "possuiPoco"); //ENUM 
        projectionList.add(Projections.property("domicilio.tipoVazaoPoco"), "tipoVazaoPoco"); //ENUM
        projectionList.add(Projections.property("domicilio.tipoVazaoPoco"), "tipoVazaoPoco"); //ENUM

        //Dados dos Moradores
        projectionList.add(Projections.property("domicilio.quantResidentes"), "quantResidentes"); //Integer
        projectionList.add(Projections.property("domicilio.quantAposentadosRecebemPensao"),
                "quantAposentadosRecebemPensao");
        projectionList.add(Projections.property("domicilio.quantPessoasRecebemBeneficio"),
                "quantPessoasRecebemBeneficio");
        projectionList.add(Projections.property("domicilio.quantMoradoresPossuemRegistroCivil"),
                "quantMoradoresPossuemRegistroCivil");
        projectionList.add(Projections.property("domicilio.quantMoradoresPossuemRG"),
                "quantMoradoresPossuemRG");
        projectionList.add(Projections.property("domicilio.quantMoradoresPossuemCPF"),
                "quantMoradoresPossuemCPF");
        projectionList.add(
                Projections.property("domicilio.quantMoradoresACima16AnosPossuemTituloEleitor"),
                "quantMoradoresACima16AnosPossuemTituloEleitor");
        projectionList.add(Projections.property("domicilio.quantPessoasTrabalham"),
                "quantPessoasTrabalham");
        projectionList.add(Projections.property("domicilio.quantPessoasNaoSabemLerOuEscrever"),
                "quantPessoasNaoSabemLerOuEscrever");

        criteria.setProjection(projectionList);
        criteria.addOrder(Order.desc(ALIAS_TIPO_LOCALIDADE)); // Order.desc Pois o 'U' vem depois do 'R'
        criteria.addOrder(Order.asc(ALIAS_LOCALIDADE));
        criteria.setResultTransformer(new AliasToBeanResultTransformer(
                RelatorioAgrupamentoLocalidadeDTO.class));

        return ColecaoChecada.checkedList(criteria.list(), RelatorioAgrupamentoLocalidadeDTO.class);
    }

    private void adicionarRestrincaoLocalidadeETipo(Criteria criteria, String localidade) {
        if (StringUtils.isNotBlank(localidade)) {
            if (TipoLocalidadeEnum.URBANA.getCodigo().equals(localidade)) {
                criteria.add(Restrictions.eq(ALIAS_TIPO_LOCALIDADE, TipoLocalidadeEnum.URBANA));

            } else if (TipoLocalidadeEnum.RURAL.getCodigo().equals(localidade)) {
                criteria.add(Restrictions.eq(ALIAS_TIPO_LOCALIDADE, TipoLocalidadeEnum.RURAL));

            } else {
                criteria.add(Restrictions.eq(ALIAS_LOCALIDADE, localidade));
            }
        }
    }

    private void createAliasDomicilio(Criteria criteria) {
        criteria.createAlias("pesquisa", "pesquisa");
        criteria.createAlias("domicilio", "domicilio");
    }

    public List<Entrevista> consultarRelatorioDetalhes(RelatorioFiltroDTO filtro) {
        Criteria criteria = createCriteria();
        criteria.createAlias("pesquisa", "pes");
        criteria.createAlias("entrevistador", "entr");
        criteria.createAlias("entrevistado", "ent");
        criteria.createAlias("domicilio", "domicilio");
        criteria.add(Restrictions.eq("pes.id", filtro.getIdPesquisa()));
        this.adicionarRestrincaoLocalidadeETipo(criteria, filtro.getLocalidade());

        criteria.addOrder(Order.desc(ALIAS_TIPO_LOCALIDADE)); // Order.desc Pois o 'U' vem depois do 'R'
        criteria.addOrder(Order.asc(ALIAS_LOCALIDADE));

        criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        return ColecaoChecada.checkedList(criteria.list(), Entrevista.class);
    }
}
